# This file is part of the Net::Tube module.
#
# Net::Tube is Free Software; you can use, redistribute it and/or
# modify it under the terms of either:
#
# a) the GNU General Public License as published by the Free Software
#    Foundation, either version 2, or (at your opinion) any later
#    version, or
#
# b) the "Artistic License" which comes with Perl.
#
package Net::Tube::Filter::Sort;

use Net::Tube;
our @ISA = qw(Net::Tube::Filter);

sub ASCENDING  { return 1; }
sub DESCENDING { return -1; }

# all other classes should override only this function
sub compare {
    my $self      = shift;    
    my $nodeA     = shift;
    my $nodeB     = shift;
    my $direction = shift;

    # return values are
    # -  0 = equal
    # - -1 = $nodeA is first
    # -  1 = $nodeB is first
    return 0;                 # this is an abstract class - all nodes are equal
}

# default parameters:
# sort-element (XPath)
# ref-element (if not then = sort-element)
# direction (ascending(1))|descending (-1))

sub direction {
    my $self = shift;
    my ($d) = $self->get_config_entry('direction');
    
    if ( defined $d && $d eq 'descending' ) {
        $d = DESCENDING();
    }

    unless ( defined $d && $d == DESCENDING() ) {
        $d = ASCENDING();
    }
    return $d;
}

# sort elements are the elements that need to get sorted
sub sort_elements {
    my $self = shift;
    my @se   = $self->get_config_entry('sort-element');
    return @se;
}

# ref_element is a reference according to which
# the nodes are sorted
sub ref_elements {
    my $self = shift;
    my @se   = $self->get_config_entry('ref-element');
    return @se;
}

sub process {
    my $self   = shift;
    my $bucket = $self->SUPER::process(@_);

    # first fetch all the nodes
    my @nodes = ();
    foreach my $se ( $self->sort_elements() ) {
        my $n = $bucket->find_nodes($se);
        if ( defined $n && scalar @$n ) {
            push @nodes, @$n;
        }
    }

    # then prepare the sort list.
    # this bit has to assure that sort operates only on
    # nodes with the same parent node.
    my @sl;
    foreach my $n (@nodes) {
        my $rn;
        foreach my $fp ( $self->ref_elements() ) {
            my @nl = $n->findnodes($fp);
            if ( scalar @nl && defined $nl[0] ) {
                $rn = $nl[0];
                last;
            }
        }
        if ( defined $rn ) {
            my $pn = $n->parentNode;
            foreach my $sln (@sl) {
                if ( $sln->[0]->isSameNode($pn) ) {
                    push @{ $sln->[1] }, [ $n, $rn ];
                    $pn = undef;
                    last;
                }
            }
            if ( defined $pn ) {
                push @sl, [ $pn, [[ $n, $rn ]] ];
            }
        }
    }

    # finally, sort!
    my $dir = $self->direction;
    foreach my $sln (@sl) {
        my $pn = $sln->[0];
        my @xl = @{ $sln->[1] };
        # do REALLY sort
        my @sorted = sort { $self->compare( $a->[1], $b->[1], $dir ) } @xl;

        # rearrange the child nodes for the parent
        foreach my $xnode (@sorted) {
            $xnode->[0]->unbindNode();
            $pn->appendChild( $xnode->[0] );
        }
    }

    return $bucket;
}

1;
